package com.pingan.agentbank.aaos.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.pingan.agentbank.aaos.exception.AAOSException;
import com.pingan.agentbank.aaos.pojo.ApproAbnormalInfo;
import com.pingan.agentbank.aaos.pojo.ApproFileInfo;
import com.pingan.agentbank.aaos.pojo.ApproLineInfo;
import com.pingan.agentbank.aaos.pojo.Config;
import com.pingan.agentbank.aaos.util.FixedLenUtil;
import com.pingan.agentbank.aaos.util.JaDateTimeUtil;
import com.pingan.dcls.util.StringUtil;
import com.pingan.vframe.db.DBIterator;
import com.pingan.vframe.db.DBTransaction;
import com.pingan.vframe.lang.Inject;
import com.pingan.vframe.logging.Log;
import com.pingan.vframe.logging.LogFactory;
import com.pingan.vframe.util.FileUtil;
import com.pingan.vframe.util.JaDateTime;
import com.pab.framework.foundation.MaskUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;


public class DataInDBService {

    private final Log log = LogFactory.getLog();
    private static final int ROW_SAVE_COUNT = 200;


    @Inject
    ApproFileDaoService approFileDaoService;
    @Inject
    ApproLineDaoService approLineDaoService;
    @Inject
    ApproAbnormalDaoService approAbnormalDaoService;




    /*
     * 新数据入库: file入库，状态为0——>line入库——>file更改状态为1
     * */
    public void perform(Config config){


        String bank = config.getBank();
        String sourDir = config.getSourDir();
        String nameFilter = config.getFileNameFilter();

        //1.查找ok文件,如果ok文件数量为0，则返回null
        log.info("[ AAOS ] "+bank+" (DBModule) "+"check new approve file");
        File file = new File(config.getSourDir());
        String[] fileStrings = file.list();

        if (fileStrings.length == 0){
            //文件夹为空，没有新文件
            log.info("[ AAOS ] "+bank+" (DBModule) "+"no approve file detected,task over");
            return;
        }

        //2.筛选名称满足过滤条件的ok文件，如果满足满足条件的ok文件数量为0，则返回null
        List<String> strList = new ArrayList();
        for (String str:fileStrings){
            //过滤所有ok文件
            if (str.matches(nameFilter)){
                strList.add(str);
            }
        }
        if (strList.size() == 0){
            //文件夹不为空，但没有.ok文件
            log.info("[ AAOS ] "+bank+" (DBModule) "+"no approve file detected,task over");
            return;
        }

        for (String str:strList){
            str = str.substring(0,str.lastIndexOf(".")) + ".txt";
            DBTransaction tran = DBTransaction.get();


            log.info("[ AAOS ] "+bank+" (DBModule) "+str+ " start insert ");
            //3.根据file config生成ApproFileInfo对象
            ApproFileInfo approFileInfo = new ApproFileInfo();
            approFileInfo.setFilename(str);
            approFileInfo.setBank(bank);
            approFileInfo.setFilepath(sourDir+"/"+str);

            //处理文件名中的日期 截取String中的日期
            String time = str.substring(0,str.lastIndexOf("-"));
            time = time.substring((time.lastIndexOf("-")+1));
            //将时间拼接成YYYYMMDDhhmiss格式
            time = time + " 000000";

            approFileInfo.setFiletime(JaDateTime.valueOf(time));

            JaDateTime jaDateTime = JaDateTimeUtil.getCurrentJaDateTime();
            approFileInfo.setCreatetime(jaDateTime);
            approFileInfo.setUpdatetime(jaDateTime);



            String msg = "";
            //5.File入库
            try {
                approFileDaoService.saveOneApproFileInfo(approFileInfo);
                tran.commitAndResume();
                log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+str+" file insert over");
            } catch (Exception e){
                msg = e.getMessage();
            }

            if (!msg.equals("SYS_DB_CONSTRAINT_VIOLATION")){
                //6.Line入库
                int count = 0;
                try {
                    count = lineInDb(approFileInfo,config);
                } catch (IOException e) {
                    log.error("[ AAOS ] "+bank+" (DBModule) "+str+" line insert "+e.getMessage());
                    throw new AAOSException("[ AAOS ] "+bank+" (DBModule) "+str+" line insert ",e);
                }
                if (count == -1){
                    return;
                }
                log.info("[ AAOS ] "+bank+" (DBModule) "+str+" line insert over");
                //7.备份
                backUp(approFileInfo,config);
                log.info("[ AAOS ] "+bank+" (DBModule) "+str+" back up over");
                //8.删除源文件及.ok文件
                deleteSour(approFileInfo,config);
                log.info("[ AAOS ] "+bank+" (DBModule) "+str+" source delete over");

                //9.更改状态
                approFileDaoService.updateCountAndStateById(count,1, (int) approFileInfo.getId());
                tran.commitAndResume();
                log.info("[ AAOS ] "+bank+" (DBModule) "+str+" change file state to 1,insert over");
            } else {

                ApproAbnormalInfo approAbnormalInfo = new ApproAbnormalInfo();
                approAbnormalInfo.setBank(approFileInfo.getBank());
                approAbnormalInfo.setFilename(approFileInfo.getFilename());
                approAbnormalInfo.setFilepath(approFileInfo.getFilepath());
                approAbnormalInfo.setCreatetime(approFileInfo.getCreatetime());
                approAbnormalInfo.setUpdatetime(approFileInfo.getUpdatetime());
                approAbnormalInfo.setFiletime(approFileInfo.getFiletime());
                approAbnormalDaoService.saveOneApproAbnormalInfo(approAbnormalInfo);
                tran.commitAndResume();

                String fileName = approAbnormalInfo.getFilename();
                //备份删除
                //备份入错误文件夹
                String fatherDir = sourDir.substring(0,sourDir.lastIndexOf("/"));
                String errorFile = fatherDir + "/" + "errorDir";
                File errorDir = new File(errorFile);
                if (!errorDir.isDirectory()){
                    errorDir.mkdir();
                }

                FileUtil.fileCopy(sourDir+"/"+fileName,errorDir+"/"+fileName);
                //写入.ok标识备份完成
                String backPath = errorDir + "/" + fileName;
                String okBackPath = backPath.substring(0,backPath.lastIndexOf(".")) + ".ok";
                FileUtil.createFile(okBackPath);
                //删除
                String sourFile = sourDir+"/"+fileName;
                String sourOkFile = sourFile.substring(0,sourFile.lastIndexOf(".")) + ".ok";
                File sourceFile = new File(sourFile);
                File sourceOkFile = new File(sourOkFile);
                sourceFile.delete();
                sourceOkFile.delete();
                log.error("[ AAOS ] "+"FileNameExistError"+" (DBModule) "+ approAbnormalInfo.getFilename() +" has exist,move to error directory");
            }
        }

        log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+" task over");
    }


    /*
     * 中断数据续传：查找状态为0的file——>line入库——>file更改状态为1
     */
    public void resume(Config config){

        //1.查找状态为0的 从逻辑上讲，一家只可能有一条
        log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+"-resume-"+" check interrupted data");

        DBIterator<ApproFileInfo> iterator =  approFileDaoService.queryByState(0);
        if (!iterator.hasNext()){
            log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+"-resume-"+" no interrupted data detected,task over");
            return;
        }
        List<ApproFileInfo> list = new ArrayList<>();
        while (iterator.hasNext()){
            list.add(iterator.next());
        }

        log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+"-resume-"+" interrupted data start resume");
        DBTransaction tran = DBTransaction.get();

        for (ApproFileInfo approFileInfo:list) {

            //System.out.println("id:"+approFileInfo.getId());
            //DBIterator只有id字段  其他字段没有,根据id查询ApproFileInfo
            ApproFileInfo approFileInfoCom = approFileDaoService.queryById(approFileInfo.getId());
            //System.out.println(approFileInfoCom);

            log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+"-resume-"+" check backup okfile");

            int count = 0;
            if (!checkBackUp(approFileInfoCom,config)){
                log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+"-resume-"+" backup okfile not exit");
                //2.删除line  可直接删除
                approLineDaoService.deleteByFileId(approFileInfoCom.getId());
                log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+"-resume-"+approFileInfoCom.getFilename()+" remaining line data delete over");

                //3.Line入库
                try {
                    count = lineInDb(approFileInfoCom,config);
                } catch (IOException e) {
                    log.error(e.getMessage());
                    e.printStackTrace();
                }
                if (count==-1){
                    return;
                }
                log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+"-resume-"+approFileInfoCom.getFilename()+" new line data insert over");

                //4.备份
                backUp(approFileInfoCom,config);
                log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+"-resume-"+approFileInfoCom.getFilename()+" back up over");

            } else {
                log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+"-resume-"+" backup okfile exist");

            }

            //5.删除源文件及.ok文件
            log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+"-resume-"+approFileInfoCom.getFilename()+" source file delete over");
            deleteSour(approFileInfoCom,config);
            //6.更改状态为1
            log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+"-resume-"+approFileInfoCom.getFilename()+" interrupted data resume success,change record data to 1");
            approFileDaoService.updateCountAndStateById(count,1, (int) approFileInfoCom.getId());
            tran.commitAndResume();
        }
        log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+"-resume-"+" interrupted data resume success,task over");

    }




    /*
    * 将文件对应的记录插入行表
    * approFileInfo 文件      config 配置
    * 返回值int 为插入数据库的行数，即文件中的记录数
    * */
    private int lineInDb(ApproFileInfo approFileInfo,Config config) throws IOException {


        File file = new File(approFileInfo.getFilepath());
        if (!file.exists()){
            log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+approFileInfo.getFilename()+" not exist,line insert over");
            return -1;
        }
        DBTransaction tran = DBTransaction.get();
        String line;
        int count = 0;


        FileInputStream inputStream = new FileInputStream(approFileInfo.getFilepath());
        InputStreamReader isr = new InputStreamReader(inputStream,config.getSourCode());
        BufferedReader reader = new BufferedReader(isr);

        while (!StringUtil.isEmpty(line = reader.readLine())){
            //校验审批必输字段
            count++;
            checkApproFileds(approFileInfo.getFilename(),count,config,line);

            //将json字符串转换为java bean
            ApproLineInfo approLineInfo = JSON.parseObject(line,new TypeReference<ApproLineInfo>(){});
            approLineInfo.setFile_id(approFileInfo.getId());
            try{
                approLineDaoService.saveOneApproLineInfo(approLineInfo);
                if (count%ROW_SAVE_COUNT == 0) {
                    tran.commitAndResume();
                }
            } catch (Exception e){
                log.error("[ AAOS ] "+config.getBank()+" (DBModule) "+approFileInfo.getFilename()+" row "+count+" line insert error");
                throw new AAOSException("AAOS_LINE_SAVE_ERROR", e);
            }
        }
        inputStream.close();
        isr.close();
        reader.close();

        tran.commitAndResume();
        return count;
    }


    /*
     * 将文件移至备份文件夹并生成对应.ok文件
     * approFileInfo 文件     config 配置
     */
    private void backUp(ApproFileInfo approFileInfo,Config config){

        File file = new File(approFileInfo.getFilepath());
        if (!file.exists()){
            log.info("[ AAOS ] "+config.getBank()+" (DBModule) "+approFileInfo.getFilename()+" not exit,back up over");
            return;
        }
        //备份
        FileUtil.fileCopy(config.getSourDir()+"/"+approFileInfo.getFilename(),config.getBackDir()+"/"+approFileInfo.getFilename());
        //写入.ok标识备份完成
        String backPath = config.getBackDir() + "/" +approFileInfo.getFilename();
        String okBackPath = backPath.substring(0,backPath.lastIndexOf(".")) + ".ok";
        FileUtil.createFile(okBackPath);
    }


    /*
     * 删除源文件及.ok文件
     * approFileInfo 文件     config 配置
     */
    private void deleteSour(ApproFileInfo approFileInfo,Config config){

        String path = approFileInfo.getFilepath();
        String okPath = path.substring(0,path.lastIndexOf(".")) + ".ok";

        new File(path).delete();
        new File(okPath).delete();

    }


    /*
     * 校验备份成功的标识.ok是否存在，ok文件存在则表示备份成功，中断数据续传时直接删除源文件就行，
     * 如果不存在，前面步骤都要再走一边（删除line表->重新插入line表->备份->删除源文件）
     * approFileInfo 文件     config 配置
     * 返回值表示是否存在
     */
    private boolean checkBackUp(ApproFileInfo approFileInfo,Config config){

        String fileName = approFileInfo.getFilename();

        String backOkFile = config.getBackDir()+"/"+fileName.substring(0,fileName.lastIndexOf("."))+".ok";
        File okFile = new File(backOkFile);
        if (okFile.exists()){
            return true;
        }
        return false;
    }

    /*
    * 校验文件中某条记录的审批字段是否为空
    * fileName 文件名  count文件中的行数  仅作为如果字段为空时，打印错误日志时定位空字段的位置
    * config 用于提供审批必输字段
    * line 文件中的行json字符串
    * */
    public void checkApproFileds(String fileName,int count,Config config,String line){

        List<String> strList = new ArrayList<>();
        //将行记录转换为JSONObject对象，即map对象
        JSONObject jsonObject = JSON.parseObject(line);
        //遍历需要校验的字段
        String[] strings = config.getApproFileds();

        for (int i=0;i<strings.length;i++){
            String field = strings[i];
            //通过key(strings[i]) 获得对应value 并判断是否为空，如果是空，则放入list
            String temp = jsonObject.getString(field);
            if (FixedLenUtil.isBlank(temp)){
                strList.add(field);
            }
        }

        if (strList.size()>0){

            StringBuffer str = new StringBuffer();
            for (String temp:strList){
                str.append(temp);
                str.append(",");
            }
            String fields = str.substring(0,str.length()-1);
            //获取行中关键信息，并脱敏
            ApproLineInfo lineInfo = JSON.parseObject(line,new TypeReference<ApproLineInfo>(){});
            String identityNo = MaskUtils.maskIdentityNo(lineInfo.getGlobal_id());
            String mobile = MaskUtils.maskMobileNo(lineInfo.getMobile());
            String approMsg = "[application_no:"+lineInfo.getApplication_no()+",chinese_name:"+lineInfo.getChinese_name()+",mobile:"+mobile+",global_id:"+identityNo+"]";

            //log.error("[ AAOS ] "+"FieldEmptyError"+" (DBModule) "+fileName+" row "+count+" fields:{"+fields+"} is empty "+approMsg);
            throw new AAOSException("[ AAOS ] "+"FieldEmptyError"+" (DBModule) "+fileName+" row "+count+" fields:{"+fields+"} is empty "+approMsg);

        }



    }
}
